import Cesium, { Cartesian2, Cartesian3, Cartographic, Scene } from '../Ces/Cesium';
import WeSurface from './WeSurface';
import WeLengthways from './WeLengthways';
import WeQueryCompute from './WeQueryCompute';

interface WeQueryOptions {
  WE: any;
}

class WeQuery {
  private WE: any;
  private _compute: WeQueryCompute;
  private _position: Cartesian3 | undefined;
  private _cartographic: Cartographic;
  private heightReference: any;
  private _toolTip: any;

  constructor(options: WeQueryOptions) {
    this.WE = options.WE;
    this._compute = new WeQueryCompute();
    this._compute.onQuery = (value: number) => this.onQuery(value);
    this._position = undefined;
    this._cartographic = new Cesium.Cartographic();
    this.heightReference = this.WE.heightReference;
  }

  onQuery(value: number): void {
    const valStr = value.toFixed(2).padEnd(15, ' ');
    let text = '';
    text = `数值: ${valStr}`;

    if (!Cesium.defined(this._toolTip)) {
      this._toolTip = this.WE.earthPinCollection.getOrCreateToolTip('WeQuery', {
        align: 'right',
      });
    }
    this._toolTip.position = this._position;
    this._toolTip.content = text;
    this._toolTip.visible = true;
  }

  findLayer(position: Cartesian3): any {
    const { WE } = this;
    const layers = WE.layerManager.weImageryLayers;

    if (layers.length > 0) {
      const cartographic = Cesium.Cartographic.fromCartesian(position, Cesium.Ellipsoid.WGS84, this._cartographic);

      for (let i = 0; i < layers.length; i++) {
        const layer = layers[i];
        if (layer.show && Cesium.Rectangle.contains(layer.rectangle, cartographic)) {
          return layer;
        }
      }
    }
    return undefined;
  }

  setupQuery(position: Cartesian3, pickObject: WeLengthways | WeSurface): Cartesian3 {
    const display = (pickObject as any)._display;
    const textureUniform = display._weTextureUniform;

    const cartographic = Cesium.Cartographic.fromCartesian(position, Cesium.Ellipsoid.WGS84, this._cartographic);
    const rectangle = display.imageryLayer.rectangle;

    const compute = this._compute;
    compute.display = display;
    compute._textureUniform = textureUniform;
    compute.query3D = true;

    const x = (cartographic.longitude - rectangle.west) / rectangle.width;
    const y = (cartographic.latitude - rectangle.south) / rectangle.height;

    let z = 0.0;
    if (pickObject instanceof WeLengthways) {
      z = cartographic.height / (pickObject.scaleZ * pickObject.extrudedHeight);
    } else if (pickObject instanceof WeSurface) {
      z = pickObject.sectPositionZ;
    }

    return new Cesium.Cartesian3(x, y, z);
  }

  pickPosition(scene: Scene, screenPosition: Cartesian2, positionScratch: Cartesian3): Cartesian3 | undefined {
    const pickedFeature = scene.pick(screenPosition);

    if (Cesium.defined(pickedFeature) && pickedFeature.id instanceof WeLengthways) {
      this._position = scene.pickPosition(screenPosition, positionScratch);
      const lengthways = pickedFeature.id;
      this._compute.pushQuery(this.setupQuery(this._position, lengthways));
    } else if (Cesium.defined(pickedFeature) && pickedFeature.id instanceof WeSurface) {
      this._position = scene.pickPosition(screenPosition, positionScratch);
      const surface = pickedFeature.id;
      this._compute.pushQuery(this.setupQuery(this._position, surface));
    } else if (this.heightReference === Cesium.HeightReference.NONE) {
      this._position = scene.camera.pickEllipsoid(
        screenPosition,
        scene.globe.ellipsoid,
        positionScratch
      );
      const layer = this.findLayer(this._position);
      if (layer) {
        this._compute.display = layer.display;
        this._compute.pushQuery(this.setupQuery(this._position, layer));
        this._compute.query3D = false;
      }
    } else {
      const ray = scene.camera.getPickRay(screenPosition);
      this._position = scene.globe.pick(ray, scene, positionScratch);
    }

    return this._position;
  }

  isDestroyed(): boolean {
    return false;
  }

  destroy(): any {
    this._compute = this._compute && this._compute.destroy();
    if (this._toolTip) {
      this.WE.earthPinCollection.remove(this._toolTip);
      this._toolTip = undefined;
    }
    return Cesium.destroyObject(this);
  }

  update(frameState): void {
    if (frameState.passes.render && Cesium.defined(this._compute)) {
      this._compute.update(frameState);
    }
  }
}

export default WeQuery;
